﻿''' <summary>
''' Supports Reading and Mounting of various types of Arphoon Package Files
''' </summary>
''' <remarks></remarks>
Partial Public Class Engine
    Implements System.IDisposable
    ' | Arps Engine                                                         |
    ' | Package Mounter and Reader                                          |
    ' | Supports Stream Reading and listing of 'Arphoon' Package Files      |
    ' | By KDERazorback                                                     |
    ' | Project Started: 24/04/2011                                         |
    ' +=====================================================================+

#If FULL_DEBUG Then
    Public Const EngineIdentifier As String = "ArpsEngine-FULLDEBUG"
#Else
    Public Const EngineIdentifier As String = "ArpsEngine"
#End If

    Public Const EngineDeveloper As String = "KDERazorback"

    Public Const DefaultShadowConvention As String = "-*.sharpoon"

    ''' <summary>
    ''' Specifies wich operations will be applied when loading Shadow packages
    ''' </summary>
    ''' <remarks></remarks>
    Public Enum ShadowAccessRights As Byte
        ''' <summary>
        ''' Shadows cannot be applied to the current Package. This parameter is useless
        ''' </summary>
        ''' <remarks></remarks>
        None = 0
        ''' <summary>
        ''' Shadows can add only new files to the base package. File overwrite and deletion are not allowed and will be ignored
        ''' </summary>
        ''' <remarks></remarks>
        Append = 1
        ''' <summary>
        ''' Shadows can delete only previously stored files on the base package. File appends or overwritting are not allowed and will be ignored
        ''' </summary>
        ''' <remarks></remarks>
        Delete = 2
        ''' <summary>
        ''' Shadows can overwrite files stored on the base package. File appends or deletes are not allowed and will be ignored
        ''' </summary>
        ''' <remarks></remarks>
        Overwrite = 4

        AppendAndDelete = 3
        AppendAndOverwrite = 5
        DeleteAndOverwrite = 6
        FullAccess = 7
    End Enum

    Public Sub New()
        ' Engine Constructor
        _loggingFacility = New Arps.Diagnostics.LogFacility
        _loggingFacility.ForceEventTriggering = True

        _protocolHDictionary = New Dictionary(Of String, MountHookDelegate)()
        _protocolHDictionary.Add("1.0.0.0_D", AddressOf _Mount_DDC1000D)
        _protocolHDictionary.Add("1.0.1.0_D", AddressOf _Mount_X1010)
        _protocolHDictionary.Add("1.0.2.0_D", AddressOf _Mount_X1020)
    End Sub

    ''' <summary>
    ''' Used to Store a list of currently known and supported Package Protocols
    ''' </summary>
    ''' <value></value>
    ''' <returns>Array of Supported Package Protocols in the form of "M.m.b.r_x" / Major Minor Build Revision Extended</returns>
    ''' <remarks></remarks>
    Public ReadOnly Property SupportedPackageProtocols As String()
        Get
            Return _supportedPackageProtocols
        End Get
    End Property
    Private _supportedPackageProtocols() As String = {"1.0.0.0_D", "1.0.1.0_D", "1.0.2.0_D"}

    ' Protocol Hooking Dictionary
    Private Delegate Function MountHookDelegate(ByVal filename As String, ByRef signature() As Byte, ByRef certCode() As Byte, ByRef reservedArea1() As Byte, ByRef protocolVersion() As Byte, ByVal hookSequence As MountSequence, ByRef data() As Byte, ByRef data2() As String, ByRef data3 As ULong) As Boolean
    Private _protocolHDictionary As Dictionary(Of String, MountHookDelegate)
    Private Function MountHookCast(ByVal filename As String, ByRef signature() As Byte, ByRef certCode() As Byte, ByRef reservedArea1() As Byte, ByRef protocolVersion() As Byte, ByVal hookSequence As MountSequence, ByRef data() As Byte, Optional ByRef data2() As String = Nothing, Optional ByRef data3 As ULong = 0) As Boolean
        If _protocolHDictionary.ContainsKey(_packageTable._package._protocolVersion) Then
            Return _protocolHDictionary(_packageTable._package._protocolVersion).Invoke(filename, signature, certCode, reservedArea1, protocolVersion, hookSequence, data, data2, data3)
        Else
            _AddLog("ERROR!: Logic Error! Protocol not found inside Hook Dictionary!")
            Return False
        End If
    End Function

    ''' <summary>
    ''' Stores all the possible return codes that can be generated by the Mount function
    ''' </summary>
    ''' <remarks></remarks>
    Public Enum PackageMountReturnCode
        MountSuccess = 0
        ioFileNotFound = 1
        ioFileEmpty = 2
        ioCantOpenFile = 3
        InvalidFileSignature = 4
        UnsupportedPackageProtocol = 5
        IncompatibleMountParameters = 6
        InvalidDescriptorJumps = 7
        DescriptorValidationFailed = 8
        InvalidDescriptorCode = 9
        AbortedByDescriptor = 10
        MissingMftJumpOffsets = 11
        InvalidMftJumps = 12
        MftValidationFailed = 13
        InvalidMft = 14
        InvalidMftCode = 15
        EmptyPackage = 16
        DatablockSizeMismatch = 17
        InvalidDatablock = 18
        InternalException = 19
    End Enum

    ' Internal variable for storing Password information
    Private _packageEncryptionPassword As String = ""
    ''' <summary>
    ''' Returns a value indicating the protocol used to compile the specified package
    ''' </summary>
    ''' <param name="filename">Filename of the package to be checked</param>
    ''' <returns>The protocol used to compile the package, as defined by the Compiler</returns>
    ''' <remarks></remarks>
    Public Function GetPackageProtocol(ByVal filename) As String

        ' Validate filename
        filename = Trim(filename)
        filename = LCase(filename)
        If filename = "" Then
            Throw New IO.FileNotFoundException("No Filename")
        End If

        ' Check if file Exists or if its Empty
        Try
            ' Check if file Exists
            Dim fi As New IO.FileInfo(filename)
            If fi.Exists = False Then
                Throw New IO.FileNotFoundException("No Filename")
            End If

            If fi.Length = 0 Then
                Throw New IO.FileNotFoundException("Empty File")
            End If

            If fi.Length <= 48 Then
                Throw New IO.InvalidDataException("Not an Arphoon Package")
            End If

            If fi.Length < 100 Then
                _AddLog("PackageRequiresMountPassword: Warning - Loading a very small file, maybe is not an Arphoon package. Continuing anyway...")
            End If
        Catch ex As Exception
            _AddLog("Mount: Exception raised while checking file at stage 1")
            _AddLog("       Maybe OS FS Layer is corrupted, driver failure, protected file, or damaged filesystem.")
            _AddLog("          Ex: " & ex.Message)
            _AddLog("       Bailed out")
            Throw New InvalidOperationException("Cant read specified file from filesystem")
        End Try

        ' Try to open file
        Dim mainFs As IO.FileStream
        Dim mainSr As IO.BinaryReader
        Try
            mainFs = New IO.FileStream(filename, IO.FileMode.Open, IO.FileAccess.Read, IO.FileShare.Read)
            mainSr = New IO.BinaryReader(mainFs)
        Catch ex As Exception
            _AddLog("Cant open specified file")
            _AddLog("   Ex: " & ex.Message)
            Throw New InvalidOperationException("Cant open specified file from filesystem")
        End Try

        ' Check file Static Signature
        'StaticSignature:
        '           Size    : 16 Bytes
        '           RAW     : 5E 41 72 70 68 6F 6F 6E 2F 2F 41 72 70 73 00 00
        '           ASCII   : ^Arphoon//Arps??
        '           Details : Identifies this file as an Arphoon Package
        '           Variants: NONE()
        Dim signature() As Byte = Nothing
        Try
            signature = mainSr.ReadBytes(16)
            If Not CompareArrays(signature, SupportedPackageSignature) Then
                _CloseStream(mainFs, mainSr)
                Throw New InvalidOperationException("Not an Arphoon Package")
            End If
        Catch ex As Exception
            _AddLog("Exception raised while checking file at stage 2")
            _AddLog("   Maybe OS FS Layer is corrupted, driver failure, protected file, damaged filesystem, or file modified while locked.")
            _AddLog("   Ex: " & ex.Message)
            _CloseStream(mainFs, mainSr)
            Throw New InvalidOperationException("Not an Arphoon Package")
        End Try

        ' Ignore file Certified Locking Code (CertCode) 2 Bytes
        mainSr.ReadBytes(2)

        ' Ignore Reserved Area 1 - 4 Bytes
        mainSr.ReadBytes(4)

        ' Check file Protocol Version
        '       ProtocolVersion:
        '           Size    : 5 Bytes
        '           RAW     : XX XX XX XX XX
        '           ASCII   : ?????
        '           Details : Specifies the Version of the Protocol used to build this package in the form of "Mmbrx", that stands for "Major Minor Build Revision Extra".
        '                     The Extra field can hold the type of build (Debug, Release, Alpha, Beta, etc) or anything else.
        '           Variants: 00 00 00 00 FF
        '                            Developer Revision. Always Load, Skip Version Checking, Use Default Processing Method
        '                     FF FF FF FF 00
        '                            Developer Revision. Always Load, Skip Version Checking, Use custom Processing Method and be totally VERBOSE
        Dim protocolVersion() As Byte = Nothing
        Try
            protocolVersion = mainSr.ReadBytes(5)
            _CloseStream(mainFs, mainSr)

            ' Check if protocol version is a debug one
            If CompareArrays(protocolVersion, {0, 0, 0, 0, 255}) Then
                ' Developer Revision 00000000FF
                Return False
            End If
            If CompareArrays(protocolVersion, {255, 255, 255, 255, 0}) Then
                ' Developer Revision FFFFFFFF00
                Return False
            End If

            ' Check protocol Version
            Dim formattedProtocolVersion As String
            formattedProtocolVersion = Hex(protocolVersion(0)) & "." & Hex(protocolVersion(1)) & "." & Hex(protocolVersion(2)) & "." & Hex(protocolVersion(3)) & "_" & Hex(protocolVersion(4))
            formattedProtocolVersion = UCase(formattedProtocolVersion)
            Dim i As Integer
            For i = 0 To SupportedPackageProtocols.Length - 1
                If formattedProtocolVersion = SupportedPackageProtocols(i) Then
                    Return SupportedPackageProtocols(i)
                End If
            Next

            ' Bad protocol
            Throw New InvalidOperationException("Unsupported Protocol")
        Catch ex As Exception
            _AddLog("Exception raised while checking file at stage 5")
            _AddLog("   Maybe OS FS Layer is corrupted, driver failure, protected file, damaged filesystem, or file modified while locked.")
            _AddLog("   Ex: " & ex.Message)
            _CloseStream(mainFs, mainSr)
            Throw New InvalidOperationException("Unsupported Protocol")
        End Try
    End Function
    Public Function HaveEncryptionSupport(ByVal protocol As String)
        Select Case protocol
            Case "1.0.2.0_D", "1.0.2.0_R"
                Return True
            Case Else
                Return False
        End Select
    End Function


    ' Direct RAW Stream and Stream Reader used to access the Package File. Every Read operation must use these resources. Except the DATABLOCK Readers that use its own resources.
    Private _ioDirectStream As IO.FileStream
    Private _ioStreamReader As IO.BinaryReader
    ''' <summary>
    ''' Checks if a file is mounted or not
    ''' </summary>
    ''' <value></value>
    ''' <returns>If true, a file is already mounted and ready.</returns>
    ''' <remarks></remarks>
    Public ReadOnly Property IsMounted() As Boolean
        Get
            Return _isMounted
        End Get
    End Property
    Private _isMounted As Boolean ' Stores a value indicating if a package is already mounted or not

    ''' <summary>
    ''' Specified if the engine will return only relative paths containing the last component of the path when searching for files and directories
    ''' </summary>
    ''' <value>Boolean value that specifies if the engine will use Relative paths</value>
    ''' <returns></returns>
    ''' <remarks>When using Relative paths, when searching for the contents of directory \dirA, the engine will return {fileA, fileB, ...fileN}. When using absolute paths (disabling this property) the enginw will return {dirA\fileA, dirA\fileB, ...dirA\fileN}</remarks>
    Public Property ReturnRelativePaths As Boolean = False ' Dont use relative paths by default

    ' Processed Data about the Package
#If FULL_DEBUG Then
    Friend _packageTable As PackageContentsCollection
#Else
    Private _packageTable As PackageContentsCollection
#End If
    ''' <summary>
    ''' Stores information about a loaded Package and its contents
    ''' </summary>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public ReadOnly Property MountedPackage() As PackageContentsCollection
        Get
            Return _packageTable
        End Get
    End Property

    ' Used to Hook Mount procedure
    Private Enum MountSequence As Byte
        ReservedArea2
        DescriptorOffset
        DescriptorSize
        ReservedArea3
        ReservedArea4
        Flags
        DescriptorTableRaw
        DescriptorTablePreprocess
        DescriptorTableLineProcess
        MftOffsetsValidation
        MftTableRaw
        MftTablePreprocess
        MftTableLineProcess
        PostStage
    End Enum

    ''' <summary>
    ''' Mounts an Arphoon Package for reading and listing
    ''' </summary>
    ''' <param name="filename">Filename of the Package that will be mounted</param>
    ''' <returns>An Enumerated value that contains whenever the Mount operation was completed, or if an error was found</returns>
    ''' <remarks>When mounting Third Party Arphoon packages, its recommended to try to mount them firsts with the most Simple flags, if any error raises, catch it and adjusts the flags in consequence. Try to avoid mounting packages with Forced Flags.</remarks>
    Public Function Mount(ByVal filename As String)
        Return Mount(filename, "")
    End Function
    ''' <summary>
    ''' Mounts an Encrypted Arphoon Package for reading and listing
    ''' </summary>
    ''' <param name="filename">Filename of the Package that will be mounted</param>
    ''' <param name="password">Specifies the password used to decrypt the package</param>
    ''' <returns>An Enumerated value that contains whenever the Mount operation was completed, or if an error was found</returns>
    ''' <remarks>When mounting Third Party Arphoon packages, its recommended to try to mount them firsts with the most Simple flags, if any error raises, catch it and adjusts the flags in consequence. Try to avoid mounting packages with Forced Flags.</remarks>
    Public Function Mount(ByVal filename As String, ByVal password As String) As PackageMountReturnCode

        ' Check if there is a file already mounted
        If _isMounted = True Then
            _AddLog("Tried to call mount again without calling unmount first. Bailed out")
            Return PackageMountReturnCode.InternalException
        End If

        ' Validate filename
        filename = Trim(filename)
        filename = LCase(filename)
        If filename = "" Then
            _AddLog("Tried to call mount without specifying any parameters. Bailed out")
            Return PackageMountReturnCode.ioFileNotFound
        End If

        ' Check if file Exists or if its Empty
        Try
            ' Check if file Exists
            Dim fi As New IO.FileInfo(filename)
            If fi.Exists = False Then
                _AddLog("Mount: ioFileNotFound > " & filename)
                Return PackageMountReturnCode.ioFileNotFound
            End If

            If fi.Length < 1 Then
                _AddLog("Mount: ioFileEmpty > " & filename)
                Return PackageMountReturnCode.ioFileEmpty
            End If

            If fi.Length <= 48 Then
                _AddLog("Mount: Tried to load a really tiny file. Not an Arphoon package. Bailed out")
                Return PackageMountReturnCode.InvalidFileSignature
            End If

            If fi.Length < 100 Then
                _AddLog("Mount: Warning - Loading a very small file, maybe is not an Arphoon package. Continuing anyway...")
            End If
        Catch ex As Exception
            _AddLog("Mount: Exception raised while checking file at stage 1")
            _AddLog("       Maybe OS FS Layer is corrupted, driver failure, protected file, or damaged filesystem.")
            _AddLog("          Ex: " & ex.Message)
            _AddLog("       Bailed out")
            Return PackageMountReturnCode.InternalException
        End Try

        'Unmount()

        _AddLog(" --- Mount Operation started")
        _AddLog("     filename: " & filename)
        _AddLog("     flags: TODO: PUT FLAGS DUMP HERE!")
        Debug.Print("TODO: PUT FLAGS DUMP AT Engine.Mount() METHOD !")

        ' Try to open file
        Try
            _ioDirectStream = New IO.FileStream(filename, IO.FileMode.Open, IO.FileAccess.Read, IO.FileShare.Read)
            _ioStreamReader = New IO.BinaryReader(_ioDirectStream)
        Catch ex As Exception
            _AddLog("Cant open specified file")
            _AddLog("   Ex: " & ex.Message)
            Return PackageMountReturnCode.ioCantOpenFile
        End Try

        ' Check file Static Signature
        'StaticSignature:
        '           Size    : 16 Bytes
        '           RAW     : 5E 41 72 70 68 6F 6F 6E 2F 2F 41 72 70 73 00 00
        '           ASCII   : ^Arphoon//Arps??
        '           Details : Identifies this file as an Arphoon Package
        '           Variants: NONE()
        Dim signature() As Byte = Nothing
        Try
            signature = _ioStreamReader.ReadBytes(16)
            If Not CompareArrays(signature, SupportedPackageSignature) Then
                _AddLog("File is NOT an Arphoon package.")
                _CloseStream(_ioDirectStream, _ioStreamReader)
                Return PackageMountReturnCode.InvalidFileSignature
            End If
        Catch ex As Exception
            _AddLog("Exception raised while checking file at stage 2")
            _AddLog("   Maybe OS FS Layer is corrupted, driver failure, protected file, damaged filesystem, or file modified while locked.")
            _AddLog("   Ex: " & ex.Message)
            _CloseStream(_ioDirectStream, _ioStreamReader)
            Return PackageMountReturnCode.InternalException
        End Try

        ' Drop the current table and make an empty one
        Try
            _packageTable = Nothing
            _packageTable = New PackageContentsCollection
            _packageTable._package._filename = filename
            _packageTable._package._size = _ioDirectStream.Length
        Catch ex As Exception
            _AddLog("Exception raised while checking file at earlier stage 3")
            _AddLog("   Maybe OS FS Layer is corrupted, driver failure, protected file, damaged filesystem, or file modified while locked.")
            _AddLog("   Ex: " & ex.Message)
            _CloseStream(_ioDirectStream, _ioStreamReader)
            Return PackageMountReturnCode.InternalException
        End Try

        ' Check file Certified Locking Code
        'CertCode:
        '           Size    : 2 Bytes
        '           RAW     : XX(XX)
        '           ASCII   : ??
        '           Details : A Partially useless code that only specify if this file can be Shadowed
        '                     The numbers for the "No Shadow Value" comes from personal memories
        '           Variants: 15 12
        '                           Shadows CANNOT be loaded
        '                     Anything else
        '                           Shadows CAN be loaded
        Dim certCode() As Byte = Nothing
        Try
            certCode = _ioStreamReader.ReadBytes(2)
            _packageTable._package._shadowCapable = Not CompareArrays(certCode, {15, 12})
        Catch ex As Exception
            _AddLog("Exception raised while checking file at stage 3")
            _AddLog("   Maybe OS FS Layer is corrupted, driver failure, protected file, damaged filesystem, or file modified while locked.")
            _AddLog("   Ex: " & ex.Message)
            _CloseStream(_ioDirectStream, _ioStreamReader)
            Return PackageMountReturnCode.InternalException
        End Try

        ' Reserved Area 1
        'Unused/Reserved Area 1:
        '           Size    : 4 Bytes
        '           RAW     : XX XX XX XX
        '           ASCII   : ????
        '           Details : An unused section that is there only for future purposes.
        Dim reservedArea1() As Byte = Nothing
        Try
            reservedArea1 = _ioStreamReader.ReadBytes(4)
        Catch ex As Exception
            _AddLog("Exception raised while checking file at stage 4")
            _AddLog("   Maybe OS FS Layer is corrupted, driver failure, protected file, damaged filesystem, or file modified while locked.")
            _AddLog("   Ex: " & ex.Message)
            _CloseStream(_ioDirectStream, _ioStreamReader)
            Return PackageMountReturnCode.InternalException
        End Try

        ' Check file Protocol Version
        '       ProtocolVersion:
        '           Size    : 5 Bytes
        '           RAW     : XX XX XX XX XX
        '           ASCII   : ?????
        '           Details : Specifies the Version of the Protocol used to build this package in the form of "Mmbrx", that stands for "Major Minor Build Revision Extra".
        '                     The Extra field can hold the type of build (Debug, Release, Alpha, Beta, etc) or anything else.
        '           Variants: 00 00 00 00 FF
        '                            Developer Revision. Always Load, Skip Version Checking, Use Default Processing Method
        '                     FF FF FF FF 00
        '                            Developer Revision. Always Load, Skip Version Checking, Use custom Processing Method and be totally VERBOSE
        Dim protocolVersion() As Byte = Nothing
        Try
            protocolVersion = _ioStreamReader.ReadBytes(5)
            ' Check if protocol version is a debug one
            If CompareArrays(protocolVersion, {0, 0, 0, 0, 255}) Then
                ' Developer Revision 00000000FF
                _AddLog("DEBUG: Developer Revision Package protocol used. 0x00000000FF")
                _packageTable._package._debugMode = True
                _packageTable._package._protocolVersion = "0.0.0.0_FF"
                Exit Try
            End If
            If CompareArrays(protocolVersion, {255, 255, 255, 255, 0}) Then
                ' Developer Revision FFFFFFFF00
                _AddLog("DEBUG: Developer Revision Package protocol used. 0xFFFFFFFF00")
                _packageTable._package._debugMode = True
                _packageTable._package._protocolVersion = "FF.FF.FF.FF_0"
                Exit Try
            End If

            ' Check protocol Version
            Dim formattedProtocolVersion As String
            formattedProtocolVersion = Hex(protocolVersion(0)) & "." & Hex(protocolVersion(1)) & "." & Hex(protocolVersion(2)) & "." & Hex(protocolVersion(3)) & "_" & Hex(protocolVersion(4))
            formattedProtocolVersion = UCase(formattedProtocolVersion)
            Dim i As Integer
            For i = 0 To SupportedPackageProtocols.Length - 1
                If formattedProtocolVersion = SupportedPackageProtocols(i) Then
                    _AddLog("Recognized Arphoon Protocol version as " & SupportedPackageProtocols(i))
                    _packageTable._package._protocolVersion = SupportedPackageProtocols(i)
                    Exit Try
                End If
            Next

            ' Bad protocol
            _AddLog("Unsupported protocol '" & formattedProtocolVersion & "' found. Aborted.")
            _CloseStream(_ioDirectStream, _ioStreamReader)
            Return PackageMountReturnCode.UnsupportedPackageProtocol
        Catch ex As Exception
            _AddLog("Exception raised while checking file at stage 5")
            _AddLog("   Maybe OS FS Layer is corrupted, driver failure, protected file, damaged filesystem, or file modified while locked.")
            _AddLog("   Ex: " & ex.Message)
            _CloseStream(_ioDirectStream, _ioStreamReader)
            Return PackageMountReturnCode.InternalException
        End Try

        ' Store Password for encrypted packages. From this point, password information is publicy available to EngineProtocols
        _packageEncryptionPassword = password
        password = ""

        ' <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< HOOKED LOGIC STARTS HERE

        ' Reserved Area 2
        '       Unused/Reserved Area 2:
        '           Size    : 4 Bytes
        '           RAW     : XX XX XX XX
        '           ASCII   : ????
        '           Details : An unused section that is there only for future purposes.
        Dim reservedArea2() As Byte = Nothing
        Try
            reservedArea2 = _ioStreamReader.ReadBytes(4)
        Catch ex As Exception
            _AddLog("Exception raised while checking file at stage 6")
            _AddLog("   Maybe OS FS Layer is corrupted, driver failure, protected file, damaged filesystem, or file modified while locked.")
            _AddLog("   Ex: " & ex.Message)
            _CloseStream(_ioDirectStream, _ioStreamReader)
            Return PackageMountReturnCode.InternalException
        End Try

        ' Execute Mount Hook Call
        ' Returns True= Abort Mounting
        ' Returns False= Continue mounting
        If MountHookCast(filename, signature, certCode, reservedArea1, protocolVersion, MountSequence.ReservedArea2, reservedArea2) Then
            _AddLog("Failed to mount target Package. Hooking Code has aborted mount sequence at stage 6")
            _CloseStream(_ioDirectStream, _ioStreamReader)
            Return PackageMountReturnCode.InternalException
        End If

        ' Check and validate the Descriptor Offset
        '       DescriptorOffset:
        '           Size    : 4 Bytes
        '           RAW     : XX XX XX XX
        '           ASCII   : ????
        '           Details : Stores the File Offset where the first Byte of the Descriptor Section is located. This offset is relative to the File Beginning.
        Dim descriptorTableOffset As ULong
        Dim descriptorTableSize As ULong

        ' Execute Mount Hook Call
        ' Returns True= Skip generic parse for DescriptorOffset
        ' Returns False= Execute generic parse for DescriptorOffset
        ' Throws an Exception= Abort with error InvalidDescriptorJumps
        Try
            If Not MountHookCast(filename, signature, certCode, reservedArea1, protocolVersion, MountSequence.DescriptorOffset, Nothing, Nothing, descriptorTableOffset) Then
                Try
                    Dim descriptorOffset() As Byte = Nothing
                    descriptorOffset = _ioStreamReader.ReadBytes(4)
                    Dim formattedOffset As ULong
                    formattedOffset = Utilities.GetUnsignedLongFromCandyArray(descriptorOffset)
                    If formattedOffset > _ioDirectStream.Length Then
                        _AddLog("Descriptor Offset Out of Bounds. Aborted")
                        _CloseStream(_ioDirectStream, _ioStreamReader)
                        Return PackageMountReturnCode.InvalidDescriptorJumps
                    End If
                    If formattedOffset < _ioDirectStream.Position + 12 Then
                        _AddLog("Descriptor Offset Out of Bounds. Aborted")
                        _CloseStream(_ioDirectStream, _ioStreamReader)
                        Return PackageMountReturnCode.InvalidDescriptorJumps
                    End If

                    descriptorTableOffset = formattedOffset
                Catch ex As Exception
                    _AddLog("Exception raised while parsing file at stage 7")
                    _AddLog("   Maybe OS FS Layer is corrupted, driver failure, protected file, damaged filesystem, or file modified while locked.")
                    _AddLog("   Ex: " & ex.Message)
                    _CloseStream(_ioDirectStream, _ioStreamReader)
                    Return PackageMountReturnCode.InternalException
                End Try
            End If
            _packageTable._package.DescriptorOffset = descriptorTableOffset
        Catch ex As Exception
            _AddLog("Failed to mount target Package. Hooking Code has aborted mount sequence at stage 7")
            _AddLog("   Ex: " & ex.Message)
            _CloseStream(_ioDirectStream, _ioStreamReader)
            Return PackageMountReturnCode.InvalidDescriptorJumps
        End Try




        ' Check and Validate Descriptor Size
        '       DescriptorSize:
        '           Size    : 4 Bytes
        '           RAW     : XX XX XX XX
        '           ASCII   : ????
        '           Details : Stores the size of the Descriptor Section in Bytes. A Package file contains at least 1 byte on the Descriptor Section.

        ' Execute Mount Hook Call
        ' Returns True= Skip generic parse for DescriptorSize
        ' Returns False= Execute generic parse for DescriptorSize
        ' Throws an Exception= Abort with error InvalidDescriptorJumps
        Try
            If Not MountHookCast(filename, signature, certCode, reservedArea1, protocolVersion, MountSequence.DescriptorSize, Nothing, Nothing, descriptorTableSize) Then
                Try
                    Dim descriptorEnd() As Byte = Nothing
                    descriptorEnd = _ioStreamReader.ReadBytes(4)
                    Dim formattedDescriptorEnd As ULong
                    formattedDescriptorEnd = Utilities.GetUnsignedLongFromCandyArray(descriptorEnd)
                    If formattedDescriptorEnd + descriptorTableOffset > _ioDirectStream.Length Then
                        _AddLog("Descriptor Offset Out of Bounds. Aborted")
                        _CloseStream(_ioDirectStream, _ioStreamReader)
                        Return PackageMountReturnCode.InvalidDescriptorJumps
                    End If
                    If formattedDescriptorEnd < 4 Then
                        _AddLog("Descriptor Offset Out of Bounds. Aborted")
                        _CloseStream(_ioDirectStream, _ioStreamReader)
                        Return PackageMountReturnCode.InvalidDescriptorJumps
                    End If

                    descriptorTableSize = formattedDescriptorEnd
                Catch ex As Exception
                    _AddLog("Exception raised while parsing file at stage 8")
                    _AddLog("   Maybe OS FS Layer is corrupted, driver failure, protected file, damaged filesystem, or file modified while locked.")
                    _AddLog("   Ex: " & ex.Message)
                    _CloseStream(_ioDirectStream, _ioStreamReader)
                    Return PackageMountReturnCode.InternalException
                End Try
            End If
            _packageTable._package.DescriptorSize = descriptorTableSize
        Catch ex As Exception
            _AddLog("Failed to mount target Package. Hooking Code has aborted mount sequence at stage 8")
            _AddLog("   Ex: " & ex.Message)
            _CloseStream(_ioDirectStream, _ioStreamReader)
            Return PackageMountReturnCode.InvalidDescriptorJumps
        End Try


        ' Reserved Area 3
        '       Unused/Reserved Area 3:
        '           Size    : 4 Bytes
        '           RAW     : XX XX XX XX
        '           ASCII   : ????
        '           Details : An unused section that is there only for future purposes. Think to be used as another Offset.
        Dim reservedArea3() As Byte = Nothing
        Try
            reservedArea3 = _ioStreamReader.ReadBytes(4)
        Catch ex As Exception
            _AddLog("Exception raised while checking file at stage 9")
            _AddLog("   Maybe OS FS Layer is corrupted, driver failure, protected file, damaged filesystem, or file modified while locked.")
            _AddLog("   Ex: " & ex.Message)
            _CloseStream(_ioDirectStream, _ioStreamReader)
            Return PackageMountReturnCode.InternalException
        End Try

        ' Execute Mount Hook Call
        ' Returns True= Abort Mounting
        ' Returns False= Continue mounting
        If MountHookCast(filename, signature, certCode, reservedArea1, protocolVersion, MountSequence.ReservedArea3, reservedArea3) Then
            _AddLog("Failed to mount target Package. Hooking Code has aborted mount sequence at stage 9")
            _CloseStream(_ioDirectStream, _ioStreamReader)
            Return PackageMountReturnCode.InternalException
        End If

        ' Reserved Area 4
        '       Unused/Reserved Area 4:
        '           Size    : 3 Bytes
        '           RAW     : XX XX XX
        '           ASCII   : ???
        '           Details : An unused section that is there only for future purposes. Think to be used as Flags and NOT as offsets or sizes.
        Dim reservedArea4() As Byte = Nothing
        Try
            reservedArea4 = _ioStreamReader.ReadBytes(3)
        Catch ex As Exception
            _AddLog("Exception raised while checking file at stage 10")
            _AddLog("   Maybe OS FS Layer is corrupted, driver failure, protected file, damaged filesystem, or file modified while locked.")
            _AddLog("   Ex: " & ex.Message)
            _CloseStream(_ioDirectStream, _ioStreamReader)
            Return PackageMountReturnCode.InternalException
        End Try

        ' Execute Mount Hook Call
        ' Returns True= Abort Mounting
        ' Returns False= Continue mounting
        If MountHookCast(filename, signature, certCode, reservedArea1, protocolVersion, MountSequence.ReservedArea4, reservedArea4) Then
            _AddLog("Failed to mount target Package. Hooking Code has aborted mount sequence at stage 10")
            _CloseStream(_ioDirectStream, _ioStreamReader)
            Return PackageMountReturnCode.InternalException
        End If

        ' Descriptor Jump Flags
        '       DescriptorJumpFlags:
        '           Size    : 1 Byte
        '           RAW     : XX
        '           ASCII   : ?
        '           Details : Flags that must be used before jumping to the Descriptor Table but after loading it into memory. For example, This fiels can specify that the Descriptor Table must be decompressed in memory before jumping to it.
        '           Variants: 00
        '                            Jump directly without performing any action
        '                     01
        '                            Skip checking against MD5
        '                     02
        '                            Decompress with Deflate before jumping
        '                     04
        '                            Decompress with GZip before jumping
        '                     08
        '                            Reserved / Unused
        '                     10
        '                            Reserved / Unused
        '                     20
        '                            Reserved / Unused
        '                     40
        '                            Reserved / Unused
        '                     80
        '                            Jump in Debug Mode (Jumps being more verbose and stops on every error, or missing parameter)
        '                     The values can me mixed, adding its values in HEX mode, Example: 04 + 01 + 08 = 0D
        '                     
        '       Total Header Size: Static - 48 Bytes
        '       Protocol Variability: ALL Protocols must use this Scheme.
        Dim descriptorJumpFlags As PackageDescriptorFlags
        Try
            Dim jumpFlags As Byte
            jumpFlags = _ioStreamReader.ReadByte()

            Dim formattedFlags As PackageDescriptorFlags
            formattedFlags = jumpFlags

            If formattedFlags And PackageDescriptorFlags.DebugMode Then
                _packageTable._package._debugMode = True
                _AddLog("DEBUG: Descriptor defined Debug Mode to ON")
            End If

            descriptorJumpFlags = formattedFlags
        Catch ex As Exception
            _AddLog("Exception raised while checking file at stage 11")
            _AddLog("   Maybe OS FS Layer is corrupted, driver failure, protected file, damaged filesystem, or file modified while locked.")
            _AddLog("   Ex: " & ex.Message)
            _CloseStream(_ioDirectStream, _ioStreamReader)
            Return PackageMountReturnCode.InternalException
        End Try

        ' Execute Mount Hook Call
        ' Returns True= Abort Mounting
        ' Returns False= Continue mounting
        Dim descriptorJumpFlagsWrapper() As Byte = {descriptorJumpFlags}
        If MountHookCast(filename, signature, certCode, reservedArea1, protocolVersion, MountSequence.Flags, descriptorJumpFlagsWrapper) Then
            _AddLog("Failed to mount target Package. Hooking Code has aborted mount sequence at stage 11")
            _CloseStream(_ioDirectStream, _ioStreamReader)
            Return PackageMountReturnCode.InternalException
        End If

        ' /////////////////////////////////////////////////////////////////////////////////////
        ' LOAD DESCRIPTOR CODE AND EXECUTE IT

        ' Reset Package Compiler Information - visuals
        _packageTable._package._compilerDetails._comments = ""
        _packageTable._package._compilerDetails._compileTime = Now
        _packageTable._package._compilerDetails._productArchitecture = PackageCompileArchitecture.Undefined
        _packageTable._package._compilerDetails._productIdentifier = "GENERIC"
        _packageTable._package._compilerDetails._productName = "Undefined"
        _packageTable._package._compilerDetails._productOwner = "Undefined"
        _packageTable._package._compilerDetails._productVersion = New Version(0, 0, 0, 0)

        Dim descriptorTableBuffer() As Byte

        ' Read the Descriptor Section
        Try
            Dim bDescriptorTable() As Byte = Nothing
            _ioDirectStream.Seek(descriptorTableOffset, IO.SeekOrigin.Begin)
            bDescriptorTable = _ioStreamReader.ReadBytes(descriptorTableSize)
            descriptorTableBuffer = bDescriptorTable
            Erase bDescriptorTable

            If descriptorTableBuffer Is Nothing Then
                _AddLog("Descriptor table is empty!")
                Return PackageMountReturnCode.InvalidDescriptorCode
            End If
        Catch ex As Exception
            _AddLog("Exception raised while reading Descriptor Table")
            _AddLog("   Maybe OS FS Layer is corrupted, driver failure, protected file, damaged filesystem, or file modified while locked.")
            _AddLog("   Ex: " & ex.Message)
            _CloseStream(_ioDirectStream, _ioStreamReader)
            Return PackageMountReturnCode.InternalException
        End Try

        ' Execute Mount Hook Call
        ' Returns True= Abort Mounting
        ' Returns False= Continue mounting
        If MountHookCast(filename, signature, certCode, reservedArea1, protocolVersion, MountSequence.DescriptorTableRaw, descriptorTableBuffer) Then
            _AddLog("Failed to mount target Package. Hooking Code has aborted mount sequence at stage 12")
            _CloseStream(_ioDirectStream, _ioStreamReader)
            Return PackageMountReturnCode.InternalException
        End If

        ' Apply actions defined by the Descriptor Flags
        Dim descriptorTable As String
        Try
            If descriptorJumpFlags = PackageDescriptorFlags.JumpDirectly Then Exit Try

            If descriptorJumpFlags And PackageDescriptorFlags.DecompressAsDeflate Then
                Dim postBuffer() As Byte
                Dim outStream As New IO.MemoryStream
                Dim mStream As New IO.MemoryStream(descriptorTableBuffer)
                Dim decompressor As IO.Compression.DeflateStream = New IO.Compression.DeflateStream(mStream, IO.Compression.CompressionMode.Decompress)
                Dim readedBytes As ULong = 1
                Dim retries As Byte = 2
                Do Until (readedBytes = 0 AndAlso retries < 1) Or (decompressor.CanRead = False)
                    Dim buffer(0) As Byte
                    readedBytes = decompressor.Read(buffer, 0, 1)
                    If readedBytes < 1 Then
                        retries -= 1
                        Continue Do
                    End If
                    retries = 2
                    outStream.Write(buffer, 0, buffer.Length)
                Loop
                decompressor.Close()
                decompressor.Dispose()
                decompressor = Nothing
                mStream.Close()
                mStream.Dispose()
                mStream = Nothing
                ReDim postBuffer(outStream.Length - 1)
                outStream.Seek(0, IO.SeekOrigin.Begin)
                outStream.Read(postBuffer, 0, outStream.Length)
                outStream.Close()
                outStream.Dispose()
                descriptorTableBuffer = postBuffer
            End If

            If descriptorJumpFlags And PackageDescriptorFlags.DecompressAsGZip Then
                Dim postBuffer() As Byte
                Dim outStream As New IO.MemoryStream
                Dim mStream As New IO.MemoryStream(descriptorTableBuffer)
                Using decompressor As IO.Compression.GZipStream = New IO.Compression.GZipStream(mStream, IO.Compression.CompressionMode.Decompress)
                    Dim readedBytes As ULong = 1
                    Do Until (readedBytes = 0) Or (decompressor.CanRead = False)
                        Dim buffer(0) As Byte
                        readedBytes = decompressor.Read(buffer, 0, 1)
                        outStream.Write(buffer, 0, buffer.Length)
                    Loop
                End Using
                mStream.Close()
                mStream.Dispose()
                mStream = Nothing
                ReDim postBuffer(outStream.Length - 1)
                outStream.Seek(0, IO.SeekOrigin.Begin)
                outStream.Read(postBuffer, 0, outStream.Length)
                outStream.Close()
                outStream.Dispose()
                descriptorTableBuffer = postBuffer
            End If

            If descriptorJumpFlags And PackageDescriptorFlags.SkipMd5Checking Then
                ' Unsupported Yet
            End If
        Catch ex As Exception
            _AddLog("Exception raised while modifying Descriptor Table in memory")
            _AddLog("   Package is corrupted.")
            _AddLog("   Ex: " & ex.Message)
            _CloseStream(_ioDirectStream, _ioStreamReader)
            Return PackageMountReturnCode.InvalidDescriptorCode
        End Try

        ' Execute Mount Hook Call
        ' Returns True= Abort Mounting
        ' Returns False= Continue mounting
        If MountHookCast(filename, signature, certCode, reservedArea1, protocolVersion, MountSequence.DescriptorTablePreprocess, descriptorTableBuffer) Then
            _AddLog("Failed to mount target Package. Hooking Code has aborted mount sequence at stage 12.2")
            _CloseStream(_ioDirectStream, _ioStreamReader)
            Return PackageMountReturnCode.InternalException
        End If

        ' Convert Descriptor Table to String
        descriptorTable = System.Text.Encoding.ASCII.GetString(descriptorTableBuffer)

        ' Execute the Descriptor Table
        Try
            Dim executionResult As PackageMountReturnCode
            executionResult = ExecuteDescriptorCode(descriptorTable)
            If Not executionResult = PackageMountReturnCode.MountSuccess Then
                _CloseStream(_ioDirectStream, _ioStreamReader)
                Return executionResult
            End If
        Catch ex As Exception
            _AddLog("Exception raised while executing the Descriptor Table in memory")
            _AddLog("   Package is corrupted.")
            _AddLog("   Ex: " & ex.Message)
            _CloseStream(_ioDirectStream, _ioStreamReader)
            Return PackageMountReturnCode.InvalidDescriptorCode
        End Try

        ' Log Compiler information and comments - visuals
        Try
            With _packageTable._package._compilerDetails
                _AddLog("Compiled by: " & ._productName & " <" & ._productIdentifier & ">.")
                _AddLog("Version " & ._productVersion.ToString)
                Dim arch As String
                Try
                    arch = Hex(._productArchitecture)
                    arch = UCase(arch)
                    If arch.Length = 1 Then arch = "0" & arch
                    arch = "0x" & arch
                Catch ex As Exception
                    arch = "Undefined"
                End Try
                _AddLog("Inside <" & arch & ">")
                _AddLog("Property of " & ._productOwner)
                _AddLog("Timestamp: " & ._compileTime.ToString(System.Globalization.CultureInfo.InvariantCulture))
                _AddLog("Comments: -->")
                _AddLog(._comments)
                _AddLog("<--")
            End With
        Catch ex As Exception
            _AddLog("Exception raised while printing Compiler details. Ignored")
            _AddLog("   Ex: " & ex.Message)
        End Try


        ' Execute Mount Hook Call
        ' Returns True= Abort Mounting
        ' Returns False= Continue mounting
        If MountHookCast(filename, signature, certCode, reservedArea1, protocolVersion, MountSequence.MftOffsetsValidation, Nothing) Then
            _AddLog("Failed to mount target Package. Hooking Code has aborted mount sequence at stage 13")
            _CloseStream(_ioDirectStream, _ioStreamReader)
            Return PackageMountReturnCode.InternalException
        End If

        ' Validate the MFT Offsets
        Try
            If _packageTable._package.MftOffset > _ioDirectStream.Length Then
                _AddLog("Mft Offsets Out of Bounds. Aborted")
                _CloseStream(_ioDirectStream, _ioStreamReader)
                Return PackageMountReturnCode.InvalidMftJumps
            End If
            If _packageTable._package.MftOffset + _packageTable._package.MftSize > _ioDirectStream.Length Then
                _AddLog("Mft Offsets Out of Bounds. Aborted")
                _CloseStream(_ioDirectStream, _ioStreamReader)
                Return PackageMountReturnCode.InvalidMftJumps
            End If
            If _packageTable.Package.MftOffset < 48 Then
                _AddLog("Mft Offsets too tiny. Aborted")
                _CloseStream(_ioDirectStream, _ioStreamReader)
                Return PackageMountReturnCode.InvalidMftJumps
            End If
            If _packageTable._package.MftSize < 16 Then
                _AddLog("Mft Size too tiny. Aborted")
                _CloseStream(_ioDirectStream, _ioStreamReader)
                Return PackageMountReturnCode.InvalidMftJumps
            End If
        Catch ex As Exception
            _AddLog("Exception raised while pre-reading MFT Table")
            _AddLog("   Package is corrupted.")
            _AddLog("   Ex: " & ex.Message)
            _CloseStream(_ioDirectStream, _ioStreamReader)
            Return PackageMountReturnCode.InvalidDescriptorCode
        End Try

        ' Read the MFT Table
        Dim mftTableBuffer() As Byte
        Try
            Dim bMftTable() As Byte = Nothing
            _ioDirectStream.Seek(_packageTable._package.MftOffset, IO.SeekOrigin.Begin)
            bMftTable = _ioStreamReader.ReadBytes(_packageTable._package.MftSize)
            mftTableBuffer = bMftTable
            Erase bMftTable

            If mftTableBuffer Is Nothing Then
                _AddLog("MFT table is empty!")
                Return PackageMountReturnCode.InvalidMftCode
            End If
        Catch ex As Exception
            _AddLog("Exception raised while reading MFT Table")
            _AddLog("   Package is corrupted.")
            _AddLog("   Ex: " & ex.Message)
            _CloseStream(_ioDirectStream, _ioStreamReader)
            Return PackageMountReturnCode.InvalidDescriptorCode
        End Try

        ' Execute Mount Hook Call
        ' Returns True= Abort Mounting
        ' Returns False= Continue mounting
        If MountHookCast(filename, signature, certCode, reservedArea1, protocolVersion, MountSequence.MftTableRaw, mftTableBuffer) Then
            _AddLog("Failed to mount target Package. Hooking Code has aborted mount sequence at stage 13.2")
            _CloseStream(_ioDirectStream, _ioStreamReader)
            Return PackageMountReturnCode.InternalException
        End If

        ' Apply MFT Flag Modifiers defined by the Descriptor Table
        Dim mftTable As String
        _packageTable._package.MftUseHash = True
        Try
            If _packageTable._package.MftJumpFlags = PackageDescriptorFlags.JumpDirectly Then Exit Try

            If _packageTable._package.MftJumpFlags And PackageDescriptorFlags.DecompressAsDeflate Then
                Dim postBuffer() As Byte
                Dim outStream As New IO.MemoryStream
                Dim mStream As New IO.MemoryStream(mftTableBuffer)
                Using decompressor As IO.Compression.DeflateStream = New IO.Compression.DeflateStream(mStream, IO.Compression.CompressionMode.Decompress)
                    Dim readedBytes As ULong = 1
                    Do Until (readedBytes = 0) Or (decompressor.CanRead = False)
                        Dim buffer(0) As Byte
                        readedBytes = decompressor.Read(buffer, 0, 1)
                        If buffer Is Nothing Then Exit Do
                        If buffer.Length = 0 Then Exit Do
                        If readedBytes = 0 Then Exit Do
                        outStream.Write(buffer, 0, buffer.Length)
                    Loop
                End Using
                mStream.Close()
                mStream.Dispose()
                mStream = Nothing
                ReDim postBuffer(outStream.Length - 1)
                outStream.Seek(0, IO.SeekOrigin.Begin)
                outStream.Read(postBuffer, 0, outStream.Length)
                outStream.Close()
                outStream.Dispose()
                mftTableBuffer = postBuffer
            End If

            If _packageTable._package.MftJumpFlags And PackageDescriptorFlags.DecompressAsGZip Then
                Dim postBuffer() As Byte
                Dim outStream As New IO.MemoryStream
                Dim mStream As New IO.MemoryStream(mftTableBuffer)
                Using decompressor As IO.Compression.GZipStream = New IO.Compression.GZipStream(mStream, IO.Compression.CompressionMode.Decompress)
                    Dim readedBytes As ULong = 1
                    Do Until (readedBytes = 0) Or (decompressor.CanRead = False)
                        Dim buffer(0) As Byte
                        readedBytes = decompressor.Read(buffer, 0, 1)
                        outStream.Write(buffer, 0, buffer.Length)
                    Loop
                End Using
                mStream.Close()
                mStream.Dispose()
                mStream = Nothing
                ReDim postBuffer(outStream.Length - 1)
                outStream.Seek(0, IO.SeekOrigin.Begin)
                outStream.Read(postBuffer, 0, outStream.Length)
                outStream.Close()
                outStream.Dispose()
                mftTableBuffer = postBuffer
            End If

            If _packageTable._package.MftJumpFlags And PackageDescriptorFlags.SkipMd5Checking Then
                _packageTable._package.MftUseHash = False
            End If
        Catch ex As Exception
            _AddLog("Exception raised while modyfing MFT Table in memory")
            _AddLog("   Package is corrupted.")
            _AddLog("   Ex: " & ex.Message)
            _CloseStream(_ioDirectStream, _ioStreamReader)
            Return PackageMountReturnCode.InvalidDescriptorCode
        End Try

        ' Execute Mount Hook Call
        ' Returns True= Abort Mounting
        ' Returns False= Continue mounting
        If MountHookCast(filename, signature, certCode, reservedArea1, protocolVersion, MountSequence.MftTablePreprocess, mftTableBuffer) Then
            _AddLog("Failed to mount target Package. Hooking Code has aborted mount sequence at stage 13.3")
            _CloseStream(_ioDirectStream, _ioStreamReader)
            Return PackageMountReturnCode.InternalException
        End If

        ' Check the MFT Table against MD5 if defined
        Try
            If _packageTable._package.MftUseHash = False Then
                _AddLog("WARNING: MFT Validation skipped.")
                Exit Try
            End If

            ' Read Hash
            Dim expectedHash() As Byte
            _ioDirectStream.Seek(_packageTable._package.MftHashOffset, IO.SeekOrigin.Begin)
            expectedHash = _ioStreamReader.ReadBytes(16)

            ' Calculate MD5
            Dim hash() As Byte
            Dim md5 As New System.Security.Cryptography.MD5CryptoServiceProvider
            hash = md5.ComputeHash(mftTableBuffer)
            md5 = Nothing

            If Utilities.CompareArrays(expectedHash, hash) = False Then
                _AddLog("MFT Table Validation failed.")
                _CloseStream(_ioDirectStream, _ioStreamReader)
                Return PackageMountReturnCode.MftValidationFailed
            End If
        Catch ex As Exception
            _AddLog("Exception raised while validating MFT Table")
            _AddLog("   Package is corrupted.")
            _AddLog("   Ex: " & ex.Message)
            _CloseStream(_ioDirectStream, _ioStreamReader)
            Return PackageMountReturnCode.InvalidDescriptorCode
        End Try

        ' Convert MFT Table to String
        mftTable = System.Text.Encoding.ASCII.GetString(mftTableBuffer)

        ' Process each instruction located inside the MFT
        Try
            Dim executionResult As PackageMountReturnCode
            executionResult = ExecuteMftCode(mftTable, filename)
            If Not executionResult = PackageMountReturnCode.MountSuccess Then
                _CloseStream(_ioDirectStream, _ioStreamReader)
                Return executionResult
            End If
        Catch ex As Exception
            _AddLog("Exception raised while processing MFT Table in memory")
            _AddLog("   Package is corrupted.")
            _AddLog("   Ex: " & ex.Message)
            _CloseStream(_ioDirectStream, _ioStreamReader)
            Return PackageMountReturnCode.InvalidDescriptorCode
        End Try

        ' Execute Mount Hook Call
        ' Returns True= Abort Mounting
        ' Returns False= Continue mounting
        If MountHookCast(filename, signature, certCode, reservedArea1, protocolVersion, MountSequence.PostStage, Nothing) Then
            _AddLog("Failed to mount target Package. Hooking Code has aborted mount sequence at end stage")
            _CloseStream(_ioDirectStream, _ioStreamReader)
            Return PackageMountReturnCode.InternalException
        End If

        ' At this point, the File structure must be ready

        ' Dispose password information. From this point, the Encryption password is no longer available
        _packageEncryptionPassword = ""

        _AddLog("Mount operation of package '" & filename & "' completed successfully.")
        _CloseStream(_ioDirectStream, _ioStreamReader)
        _isMounted = True
        _packageTable._package._name = (New IO.FileInfo(filename).Name)

        ' Calculate packed size
        Dim size As ULong = 0
        If _packageTable._file IsNot Nothing Then
            For Each file As PackagedFileInformationCollection In _packageTable._file
                If Not file.Filename = "" Then
                    size += file.Length
                End If
            Next
        End If
        _packageTable._package._dataSize = size

        Return PackageMountReturnCode.MountSuccess
    End Function

    ''' <summary>
    ''' Enumerates all loadable shadows that follows the default file naming convention
    ''' </summary>
    ''' <returns>A sortered list of shadow files that can be loaded for the current package</returns>
    ''' <remarks>A file convention is merely a regex file filter used to search for filenames on the active directory. Default convention: "@basePackageName@-*.sharpoon" where "@basePackageName@" is the base package filename without the extension</remarks>
    Public Function EnumerateShadows() As List(Of String)
        Dim fi As New System.IO.FileInfo(_packageTable._package.Name)
        Dim name As String = fi.Name.Substring(0, fi.Name.Length - fi.Extension.Length)

        Return EnumerateShadows(name & DefaultShadowConvention)
    End Function
    ''' <summary>
    ''' Enumerates all loadable shadows that follows an specified file naming convention
    ''' </summary>
    ''' <param name="convention">Specified file naming convention for shadow files</param>
    ''' <returns>A sortered list of shadow files that can be loaded for the current package</returns>
    ''' <remarks>A file convention is merely a regex file filter used to search for filenames on the active directory.</remarks>
    Public Function EnumerateShadows(ByVal convention As String) As List(Of String)
        If Not IsMounted Then
            _AddLog("Tried to call EnumerateShadows when no package is mounted. Bailed out")
            Throw New NotSupportedException("Enumeration not possible without a mounted package")
        End If

        Try
            Dim fi As New System.IO.FileInfo(_packageTable.Package.Filename)
            Dim dir As System.IO.DirectoryInfo = fi.Directory
            Dim files() As System.IO.FileInfo = dir.GetFiles(convention)

            If files Is Nothing OrElse files.Length < 1 Then
                Return Nothing
            End If

            Dim lst As New List(Of String)(files.Length)

            Dim i As Long
            For i = 0 To files.Length - 1
                lst.Add(files(i).Name)
            Next

            files = Nothing
            dir = Nothing
            fi = Nothing

            lst.Sort(New StringLogicalComparer())

            Return lst
        Catch ex As Exception
            _AddLog("Failed to enumerate possible shadow files for current package! Exception raised. Bailed out")
            _AddLog("    Ex: " & ex.Message)
            _AddLog("    Convention: " & convention)
            Return Nothing
        End Try
    End Function
    ''' <summary>
    ''' Attaches the specified shadow file to the currently mounted Arphoon Package
    ''' </summary>
    ''' <param name="filename">Filename of the shadow file to attach</param>
    ''' <param name="password">Optional password for mounting the shadow file</param>
    ''' <param name="access">Desired access for the shadow file</param>
    ''' <returns>A value indicating if the attaching process succeeded</returns>
    ''' <remarks>Shadows can only be mounted if the base arphoon package is marked as shadowable</remarks>
    Public Function AttachShadow(ByVal filename As String, ByVal password As String, ByVal access As ShadowAccessRights) As PackageMountReturnCode
        Return _AttachShadow(filename, password, access)
    End Function
    ''' <summary>
    ''' Searches and attaches to the current mounted package, all shadows that match the default file naming convention. With the specified password and access rights
    ''' </summary>
    ''' <param name="password">Password used to mount shadow files</param>
    ''' <param name="access">Desired access for the mounted shadows</param>
    ''' <remarks></remarks>
    Public Sub AttachAllShadows(ByVal password As String, ByVal access As ShadowAccessRights)
        Dim filesLst As List(Of String)
        filesLst = EnumerateShadows()
        If filesLst Is Nothing OrElse filesLst.Count < 1 Then Exit Sub

        Dim files() As String = filesLst.ToArray()
        filesLst.Clear()

        Dim i As Long
        For i = 0 To files.Length - 1
            AttachShadow(files(i), password, access)
        Next
    End Sub
    ''' <summary>
    ''' Searches and attaches to the current mounted package, all shadows that match the specified file naming convention. With the specified password and access rights
    ''' </summary>
    ''' <param name="convention">File naming convention that all shadows must follow to be loaded</param>
    ''' <param name="password">Password used to mount shadow files</param>
    ''' <param name="access">Desired access for the mounted shadows</param>
    ''' <remarks></remarks>
    Public Sub AttachAllShadows(ByVal convention As String, ByVal password As String, ByVal access As ShadowAccessRights)
        Dim filesLst As List(Of String)
        filesLst = EnumerateShadows(convention)
        If filesLst Is Nothing OrElse filesLst.Count < 1 Then Exit Sub

        Dim files() As String = filesLst.ToArray()
        filesLst.Clear()

        Dim i As Long
        For i = 0 To files.Length - 1
            AttachShadow(files(i), password, access)
        Next
    End Sub

    Protected Friend Function _AttachShadow(ByVal filename As String, ByVal password As String, ByVal access As ShadowAccessRights) As PackageMountReturnCode
        Dim returned As PackageMountReturnCode
        Dim tmpEngine = New Engine()

        _AddLog(" --- Shadow Attach operation started")
        _AddLog("     filename: " & filename)
        _AddLog("     permissions: " & access.ToString())

        returned = tmpEngine.Mount(filename, password)

        If Not returned = PackageMountReturnCode.MountSuccess Then
            ' Report failure
            _AddLog("Attach: Failed to attach an specified filename as shadow package.")
            _AddLog("   Code: " & returned.ToString())
            _AddLog("   Filename: " & filename)
        Else
            ' Report success and patch _packageTable
            _AddLog("Shadow package successfully mounted. Patching MFT...")

            Dim contents As List(Of PackagedFileInformationCollection) = tmpEngine._packageTable._file
            Dim shadowProfile = New LoadedShadowCollection()
            shadowProfile._debugMode = tmpEngine._packageTable._package.DebugMode
            shadowProfile._filename = tmpEngine._packageTable._package.Filename
            shadowProfile._protocolVersion = tmpEngine._packageTable._package.ProtocolVersion
            If _packageTable._package._loadedShadow Is Nothing Then
                shadowProfile._shadowLayer = 0
                _packageTable._package._loadedShadow = New List(Of LoadedShadowCollection)(16)
            Else
                shadowProfile._shadowLayer = _packageTable._package._loadedShadow.Count
            End If
            shadowProfile._size = tmpEngine._packageTable._package.Size
            shadowProfile._storedFiles = tmpEngine._packageTable._file

            _packageTable._package._loadedShadow.Add(shadowProfile)
            tmpEngine.Dispose()

            Dim i As Long
            Dim x As Long
            Dim appendedCount As Long = 0
            Dim deletedCount As Long = 0
            Dim overwrittedCount As Long = 0
            For i = 0 To contents.Count - 1
                Dim lcName As String = contents(i).FullName.ToLowerInvariant()
                Dim found As Boolean = False

                If _packageTable._file.Count > 0 Then
                    For x = 0 To _packageTable._file.Count - 1
                        If _packageTable._file(x).FullName.ToLowerInvariant() = lcName Then
                            found = True
                            If contents(i).Md5Hash.ToLowerInvariant() = "delete" Then
                                ' Delete entry
                                If (access And ShadowAccessRights.Delete) = ShadowAccessRights.Delete Then
                                    _packageTable._file.RemoveAt(x)
                                    deletedCount += 1
                                End If
                            Else
                                ' Overwrite entry
                                If (access And ShadowAccessRights.Overwrite) = ShadowAccessRights.Overwrite Then
                                    overwrittedCount += 1
                                    _packageTable._file(x) = contents(i)
                                End If
                            End If
                            Exit For
                        End If
                    Next
                End If

                If Not found AndAlso (Not contents(i).Md5Hash.ToLowerInvariant() = "delete") Then
                    ' Append entry
                    If (access And ShadowAccessRights.Append) = ShadowAccessRights.Append Then
                        appendedCount += 1
                        _packageTable._file.Add(contents(i))
                    End If
                End If
            Next

            _AddLog(overwrittedCount & " files overwritted.")
            _AddLog(deletedCount & " files deleted.")
            _AddLog(appendedCount & " files added.")

            _fileTreeBuilt = False
            _directoryTreeBuilt = False
        End If


        Return returned
    End Function

    ''' <summary>
    ''' Unmounts any currently mounted Arphoon package and its loaded Shadows
    ''' </summary>
    ''' <remarks>This method always ensures that this instance of the Engine gets cleared and ready for another Mount operation</remarks>
    Public Sub Unmount()
        ' Umounts any currently mounted package

        On Error Resume Next

        If _isMounted = False Then
            _AddLog("Tried to unmount a package that is not mounted. Ignored")
            Exit Sub
        End If

        _AddLog("Unmounting package...")
        _CloseStream(_ioDirectStream, _ioStreamReader)
        _packageTable = Nothing
        _isMounted = False
        _AddLog("Done.")
    End Sub

    Private Function ExecuteDescriptorCode(ByVal descriptorTable As String) As PackageMountReturnCode
        ' Executes the code stored inside the specified Descriptor Table
        ' Returns a code indicating if the operation was successfull, or if has failed by some reason
        ' A Descriptor Table is an HGL like script

        descriptorTable = Trim(descriptorTable)
        If descriptorTable = "" Then Return PackageMountReturnCode.InvalidDescriptorCode

        Dim lines() As String
        ' Split by lines
        Try
            lines = Split(descriptorTable, Chr(13) & Chr(10))
        Catch ex As Exception
            _AddLog(" >Failed to split the descriptor table")
            Return PackageMountReturnCode.InvalidDescriptorCode
        End Try

        ' Process each line
        Dim line As Integer
        Dim localVariables(31) As String ' Max Local Variables that can be used by the Descriptor Table
        For line = 0 To lines.Length - 1
            Dim command As String
            Dim params() As String
            lines(line) = Trim(lines(line))
            If lines(line) = "" Then GoTo NextLine
            Try
                Dim tmpInstruction() As String
                ' Expand Variables
                lines(line) = _ExpandEngineVariables(lines(line), localVariables)
                tmpInstruction = Split(lines(line), Chr(4))
                command = tmpInstruction(0)
                If tmpInstruction.Length > 1 Then
                    ReDim params(tmpInstruction.Length - 2)
                    Array.ConstrainedCopy(tmpInstruction, 1, params, 0, tmpInstruction.Length - 1)
                Else
                    params = Nothing
                End If
                command = LCase(command)
                command = Trim(command)
            Catch ex As Exception
                _AddLog(" >Failed to parse descriptor table at line: " & line)
                If _packageTable._package._debugMode = True Then
                    _AddLog("Debug mode is ON. Warnings are not allowed")
                    Return PackageMountReturnCode.InvalidDescriptorCode
                End If
                GoTo NextLine
            End Try

            Try
                ' Execute Mount Hook Call
                ' Returns True= Skip Line
                ' Returns False= Process Line normally
                ' Throws an Exception= Abort Mounting
                Dim lineBuffer() As String
                If (params Is Nothing) OrElse (params.Length < 1) Then
                    ReDim lineBuffer(0)
                Else
                    ReDim lineBuffer(params.Length)
                    Array.ConstrainedCopy(params, 0, lineBuffer, 1, params.Length)
                End If
                lineBuffer(0) = command
                If MountHookCast(Nothing, Nothing, Nothing, Nothing, Nothing, MountSequence.DescriptorTableLineProcess, Nothing, lineBuffer) Then
                    GoTo NextLine
                Else
                    command = lineBuffer(0)
                    If lineBuffer.Length > 1 Then
                        ReDim params(lineBuffer.Length - 2)
                        Array.ConstrainedCopy(lineBuffer, 1, params, 0, params.Length)
                    Else
                        Erase params
                    End If
                End If

                Select Case command
                    Case "#", "comment"
                        ' Ignore
                    Case "if"
                        ' Check param0=param1 and if True jump tu param2
                        If params(0) = params(1) Then line = params(2)
                    Case "set"
                        ' Set var(param0) to param1
                        Dim index As Integer
                        Try
                            index = Integer.Parse(params(0))
                            localVariables(params(0)) = params(1)
                        Catch ex As Exception
                            _AddLog("Invalid index for parameter varID at line " & line)
                            If _packageTable._package._debugMode = True Then
                                _AddLog("Debug mode is ON. Warnings are not allowed")
                                Return PackageMountReturnCode.InvalidDescriptorCode
                            End If
                        End Try
                    Case "abort"
                        ' Abort the Operation
                        _AddLog("Mount operation aborted by the Arphoon Package Descriptor.")
                        _AddLog(" >" & params(0))
                        Return PackageMountReturnCode.AbortedByDescriptor
                    Case "quit"
                        ' End Descriptor Processing
                        If params Is Nothing OrElse params(0).Length = 0 OrElse params(0) = 0 Then
                            Return PackageMountReturnCode.MountSuccess
                        Else
                            _AddLog("Script ended silently with a custom exit code")
                            _AddLog("Code: " & params(1))
                            If _packageTable._package._debugMode = True Then
                                _AddLog("Debug mode is ON. Warnings are not allowed")
                                Return PackageMountReturnCode.InvalidDescriptorCode
                            End If
                            Return PackageMountReturnCode.MountSuccess
                        End If
                    Case "mft_offset"
                        ' Sets the Offset pointing to the first byte of the MFT Table
                        Dim newAddress As ULong
                        Try
                            newAddress = params(0)
                            _packageTable._package.MftOffset = newAddress
                        Catch ex As Exception
                            _AddLog("Specified Mft Offsets are invalid!")
                            Return PackageMountReturnCode.InvalidMftJumps
                        End Try
                    Case "mft_size"
                        ' Sets the size of the MFT Table
                        Dim newAddress As ULong
                        Try
                            newAddress = params(0)
                            _packageTable._package.MftSize = newAddress
                        Catch ex As Exception
                            _AddLog("Specified Mft Offsets are invalid!")
                            Return PackageMountReturnCode.InvalidMftJumps
                        End Try
                    Case "md5offset"
                        ' Sets the offset of the Uncompressed MFT MD5 Hash
                        Dim newAddress As ULong
                        Try
                            newAddress = params(0)
                            _packageTable._package.MftHashOffset = newAddress
                        Catch ex As Exception
                            _AddLog("Specified Mft Hash Offsets are invalid")
                            If _packageTable._package._debugMode = True Then
                                _AddLog("Debug mode is ON. Warnings are not allowed")
                                Return PackageMountReturnCode.InvalidDescriptorCode
                            End If
                        End Try
                    Case "flags", "mft_flags"
                        ' Sets the Jump flags of the MFT Table
                        Dim flags As Integer
                        Try
                            flags = params(0)
                            _packageTable._package.MftJumpFlags = flags
                        Catch ex As Exception
                            _AddLog("Specified Mft flags are invalid!")
                            Return PackageMountReturnCode.InvalidMftJumps
                        End Try
                    Case "log"
                        ' Adds some text to the Debug Output
                        Dim text As String
                        Try
                            text = params(0)
                            text = "   [DESC]{code} " & text
                            _AddLog(text)
                        Catch ex As Exception
                            _AddLog("Invalid 'log' call at line " & line)
                            If _packageTable._package._debugMode = True Then
                                _AddLog("Debug mode is ON. Warnings are not allowed")
                                Return PackageMountReturnCode.InvalidDescriptorCode
                            End If
                        End Try
                    Case "compiler"
                        ' Sets the Compiler Product Name
                        Dim newName As String
                        Try
                            newName = params(0)
                            newName = Trim(newName)
                            If newName = "" Then newName = "Undefined"
                            _packageTable._package._compilerDetails._productName = newName
                        Catch ex As Exception
                            _AddLog("Invalid call at line " & line)
                            If _packageTable._package._debugMode = True Then
                                _AddLog("Debug mode is ON. Warnings are not allowed")
                                Return PackageMountReturnCode.InvalidDescriptorCode
                            End If
                        End Try
                    Case "compiler_version"
                        ' Sets the Version of the compiler used
                        Dim newVersion As Version
                        Dim rawVersion As String
                        Try
                            rawVersion = params(0)
                            rawVersion = Trim(rawVersion)
                            newVersion = New Version(rawVersion)
                            _packageTable._package._compilerDetails._productVersion = newVersion
                        Catch ex As Exception
                            _AddLog("Invalid call at line " & line)
                            If _packageTable._package._debugMode = True Then
                                _AddLog("Debug mode is ON. Warnings are not allowed")
                                Return PackageMountReturnCode.InvalidDescriptorCode
                            End If
                        End Try
                    Case "compiler_arch"
                        ' Sets the Architecture where the compiler has been used
                        Dim newArch As PackageCompileArchitecture
                        Try
                            params(0) = Trim(params(0))
                            newArch = params(0)
                            If newArch > 9 Then newArch = PackageCompileArchitecture.Undefined
                            _packageTable._package._compilerDetails._productArchitecture = newArch
                        Catch ex As Exception
                            _AddLog("Invalid call at line " & line)
                            If _packageTable._package._debugMode = True Then
                                _AddLog("Debug mode is ON. Warnings are not allowed")
                                Return PackageMountReturnCode.InvalidDescriptorCode
                            End If
                        End Try
                    Case "compiler_owner"
                        ' Sets the Compiler Owner
                        Dim newOwner As String
                        Try
                            params(0) = Trim(params(0))
                            newOwner = params(0)
                            _packageTable._package._compilerDetails._productOwner = newOwner
                        Catch ex As Exception
                            _AddLog("Invalid call at line " & line)
                            If _packageTable._package._debugMode = True Then
                                _AddLog("Debug mode is ON. Warnings are not allowed")
                                Return PackageMountReturnCode.InvalidDescriptorCode
                            End If
                        End Try
                    Case "compiler_id"
                        ' Sets the Internal Identifier of the Compiler used
                        Dim newIdentifier As String
                        Try
                            params(0) = Trim(params(0))
                            params(0) = UCase(params(0))
                            newIdentifier = params(0)
                            _packageTable._package._compilerDetails._productIdentifier = newIdentifier
                        Catch ex As Exception
                            _AddLog("Invalid call at line " & line)
                            If _packageTable._package._debugMode = True Then
                                _AddLog("Debug mode is ON. Warnings are not allowed")
                                Return PackageMountReturnCode.InvalidDescriptorCode
                            End If
                        End Try
                    Case "compile_time"
                        ' Sets the compile time of the package
                        Dim newTimestamp As Date
                        Try
                            params(0) = Trim(params(0))
                            newTimestamp = Date.Parse(params(0), System.Globalization.CultureInfo.InvariantCulture)
                            _packageTable._package._compilerDetails._compileTime = newTimestamp
                        Catch ex As Exception
                            _AddLog("Invalid call at line " & line)
                            If _packageTable._package._debugMode = True Then
                                _AddLog("Debug mode is ON. Warnings are not allowed")
                                Return PackageMountReturnCode.InvalidDescriptorCode
                            End If
                        End Try
                    Case "compile_cadd"
                        ' Adds a line to the Comments section
                        Dim newLine As String
                        Try
                            newLine = params(0)
                            newLine = RTrim(params(0))
                            With _packageTable._package._compilerDetails
                                If ._comments = "" Then
                                    ._comments = newLine
                                Else
                                    ._comments &= Chr(13) & Chr(10) & newLine
                                End If
                            End With
                        Catch ex As Exception
                            _AddLog("Invalid call at line " & line)
                            If _packageTable._package._debugMode = True Then
                                _AddLog("Debug mode is ON. Warnings are not allowed")
                                Return PackageMountReturnCode.InvalidDescriptorCode
                            End If
                        End Try
                    Case Else
                        _AddLog("Unrecognized Descriptor Code at line: " & line)
                        If _packageTable._package._debugMode = True Then
                            _AddLog("Debug mode is ON. Warnings are not allowed")
                            Return PackageMountReturnCode.InvalidDescriptorCode
                        End If
                End Select
            Catch ex As Exception
                _AddLog(" >Failed to process descriptor table at line: " & line)
                If _packageTable._package._debugMode = True Then
                    _AddLog("Debug mode is ON. Warnings are not allowed")
                    Return PackageMountReturnCode.InvalidDescriptorCode
                End If
            End Try
NextLine:
        Next

        _AddLog("Package Descriptor ends without calling a valid ender.")
        If _packageTable._package._debugMode = True Then
            _AddLog("Debug mode is ON. Warnings are not allowed")
            Return PackageMountReturnCode.InvalidDescriptorCode
        Else
            Return PackageMountReturnCode.MountSuccess
        End If
    End Function
    Private Function ExecuteMftCode(ByVal mftTable As String, ByVal masterPath As String) As PackageMountReturnCode
        ' Executes the code stored inside an MFT

        mftTable = Trim(mftTable)
        If mftTable = "" Then Return PackageMountReturnCode.InvalidMftCode

        Dim lines() As String
        ' Split by lines
        Try
            lines = Split(mftTable, Chr(13) & Chr(10))
        Catch ex As Exception
            _AddLog(" >Failed to split the MFT table")
            Return PackageMountReturnCode.InvalidMftCode
        End Try

        ' Process each line
        Dim line As Integer
        For line = 0 To lines.Length - 1
            Dim command As String
            Dim params() As String
            lines(line) = Trim(lines(line))
            If lines(line) = "" Then GoTo NextLine
            Try
                Dim tmpInstruction() As String
                tmpInstruction = Split(lines(line), Chr(4))
                command = tmpInstruction(0)
                If tmpInstruction.Length > 1 Then
                    ReDim params(tmpInstruction.Length - 2)
                    Array.ConstrainedCopy(tmpInstruction, 1, params, 0, tmpInstruction.Length - 1)
                Else
                    params = Nothing
                End If
                command = LCase(command)
                command = Trim(command)
            Catch ex As Exception
                _AddLog(" >Failed to parse MFT table at line: " & line)
                If _packageTable._package._debugMode = True Then
                    _AddLog("Debug mode is ON. Warnings are not allowed")
                    Return PackageMountReturnCode.InvalidMftCode
                End If
                GoTo NextLine
            End Try

            ' Execute Mount Hook Call
            ' Returns True= Skip Line
            ' Returns False= Process
            ' Throws an Exception= Abort Mounting
            Dim lineBuffer() As String
            If (params Is Nothing) OrElse (params.Length < 1) Then
                ReDim lineBuffer(0)
            Else
                ReDim lineBuffer(params.Length)
                Array.ConstrainedCopy(params, 0, lineBuffer, 1, params.Length)
            End If
            lineBuffer(0) = command
            If MountHookCast(Nothing, Nothing, Nothing, Nothing, Nothing, MountSequence.MftTableLineProcess, Nothing, lineBuffer) Then
                GoTo NextLine
            Else
                command = lineBuffer(0)
                If lineBuffer.Length > 1 Then
                    ReDim params(lineBuffer.Length - 2)
                    Array.ConstrainedCopy(lineBuffer, 1, params, 0, params.Length)
                Else
                    Erase params
                End If
            End If

            Select Case command
                Case "#", "comment"
                    ' Ignore
                Case "file"
                    Dim md5 As String
                    Dim offset As ULong
                    Dim size As ULong
                    Dim fullFilename As String
                    If params Is Nothing Then
                        _AddLog(" >Failed to map file onto the Package Surface.")
                        _AddLog("  Invalid call at line " & line)
                        If _packageTable._package._debugMode = True Then
                            _AddLog("Debug mode is ON. Warnings are not allowed")
                            Return PackageMountReturnCode.InvalidMftCode
                        End If
                        GoTo NextLine
                    End If

                    Try
                        fullFilename = params(0)
                        offset = params(1) + _packageTable._package.DataBlockOffset
                        size = params(2)
                        If params.Length > 3 Then
                            md5 = params(3)
                        Else
                            md5 = ""
                        End If

                        fullFilename = Trim(fullFilename)

                        If _packageTable._file Is Nothing Then
                            _packageTable._file = New System.Collections.Generic.List(Of PackagedFileInformationCollection)(lines.Length)
                            'Else
                            '    ReDim Preserve _packageTable._file(_packageTable._file.Length)
                        End If

                        Dim filename As String
                        Dim extension As String
                        Dim directory As String
                        Try
                            Dim tmpPath() As String
                            tmpPath = Split(fullFilename, "\")
                            filename = tmpPath(tmpPath.Length - 1)
                        Catch ex As Exception
                            filename = "null"
                        End Try
                        Try
                            Dim tmpFilename() As String
                            tmpFilename = Split(filename, ".")
                            extension = tmpFilename(tmpFilename.Length - 1)
                        Catch ex As Exception
                            extension = ""
                        End Try
                        directory = Left(fullFilename, fullFilename.Length - filename.Length)
                        directory = Trim(directory)
                        If directory = "" Then directory = "\"

                        Dim newItem As New PackagedFileInformationCollection()
                        With newItem
                            ._directory = directory
                            ._extension = extension
                            ._filename = filename
                            ._fullName = fullFilename
                            ._internalSize = size
                            ._length = size
                            ._md5Hash = md5
                            ._packageFlags = 0
                            ._packageOffset = offset
                            ._packagePath = masterPath
                            '_AddLog("Mapped file: '" & filename & "' with path '" & directory & "' onto Package surface.")
                        End With
                        _packageTable._file.Add(newItem)
                    Catch ex As Exception
                        _AddLog(" >Failed to map file onto the Package Surface.")
                        _AddLog("  Invalid call at line " & line)
                        If _packageTable._package._debugMode = True Then
                            _AddLog("Debug mode is ON. Warnings are not allowed")
                            Return PackageMountReturnCode.InvalidMftCode
                        End If
                        GoTo NextLine
                    End Try
                Case "log"
                    ' Adds some text to the Debug Output
                    Dim text As String
                    Try
                        text = params(0)
                        text = "   [MFT]{code} " & text
                        _AddLog(text)
                    Catch ex As Exception
                        _AddLog(" >Failed to process call at line " & line & ".")
                        If _packageTable._package._debugMode = True Then
                            _AddLog("Debug mode is ON. Warnings are not allowed")
                            Return PackageMountReturnCode.InvalidDescriptorCode
                        End If
                    End Try
            End Select
NextLine:
        Next

        Return PackageMountReturnCode.MountSuccess
    End Function

    Private Function _ExpandEngineVariables(ByVal code As String, ByVal localVariables() As String) As String
        ' Expands Engine global Variables in the form of :__<var>: to its representations

        code = Replace(code, ":__ENGINE_ID:", EngineIdentifier)
        code = Replace(code, ":__ENGINE_OWNER:", EngineDeveloper)
        code = Replace(code, ":__ENGINE_VER_MAJOR:", My.Application.Info.Version.Major)
        code = Replace(code, ":__ENGINE_VER_MINOR:", My.Application.Info.Version.Minor)
        code = Replace(code, ":__ENGINE_VER_BUILD:", My.Application.Info.Version.Build)
        code = Replace(code, ":__ENGINE_VER_REV:", My.Application.Info.Version.Revision)
        code = Replace(code, ":__PACKAGE_PATH:", _packageTable._package.Filename)
        code = Replace(code, ":__ISDEBUG:", Utilities._UnifyBooleanValue(_packageTable._package._debugMode))
        code = Replace(code, ":__PROTOCOL:", _packageTable._package._protocolVersion)
        code = Replace(code, ":__SIZE:", _packageTable._package._size)

        Dim i As Integer
        For i = 0 To localVariables.Length - 1
            code = Replace(code, ":__v" & i & ":", localVariables(i))
        Next

        Return code
    End Function

    ' Used to Log events and store the Output in this class
    Private WithEvents _loggingFacility As Arps.Diagnostics.LogFacility

    Friend Sub _AddLog(ByVal text As String)
        _loggingFacility.AddLog(text, True)
    End Sub

    Private Sub _CloseStream(ByRef ioStream As IO.FileStream, ByRef ioStreamReader As IO.BinaryReader)
        ' Used to close an Stream Reader and a FileStream without generating any Exception

        On Error Resume Next
        If ioStreamReader IsNot Nothing Then
            ioStreamReader.Close()
            ioStreamReader = Nothing
        End If
        If ioStream IsNot Nothing Then
            ioStream.Close()
            ioStream.Dispose()
            ioStream = Nothing
        End If
    End Sub

    Public Event LogChanged(ByVal text As String)
    Private Sub LogFacilityChangedHandler(ByVal text As String) Handles _loggingFacility.FacilityChanged
        RaiseEvent LogChanged(text)
    End Sub
    Public ReadOnly Property LogFacility() As String
        Get
            Return _loggingFacility.GetFacility
        End Get
    End Property

    ' Directory and File Tree structure virtualizers
    Public Function GetDirectories(ByVal path As String, ByVal filter As String) As String()

        ' Process and Validate Path
        path = LCase(path)
        path = Replace(path, "/", "\")
        path = Replace(path, "\\", "\")
        If Left(path, 1) = "\" Then path = Right(path, path.Length - 1) ' Remove any heading '\'
        If Right(path, 1) = "\" Then path = Left(path, path.Length - 1) ' Remove any leading '\'

        ' Get the ammount of levels that Path have
        Dim level As String
        Dim temp() As String
        temp = Split(path, "\")
        level = temp.Length
        Erase temp

        ' Check the special case of "\" or empty path
        If path Is Nothing Then level = 0
        If path = "" Then level = 0

        If _directoryTreeBuilt = False Then BuildDirectoryTreeStructure() ' Build Directory structure if its not built before

        If _formattedDirectoryTree Is Nothing Then Return Nothing ' No directories stored on the current package

        If level >= _formattedDirectoryTree.Length - 1 Then Return Nothing ' No directories with that level

        filter = Trim(filter)
        If filter = "" Then filter = "*"

        Dim directories() As String = _formattedDirectoryTree(level + 1)
        If directories Is Nothing Then Return Nothing
        Dim output() As String = Nothing
        For Each entry As String In directories
            entry = Trim(entry)
            If Not entry = "" Then
                Dim query As String = path
                If Not path = "" Then query &= "\"
                query &= filter
                query = Replace(query, "[", "[[]")
                If LCase(entry) Like LCase(query) Then
                    If output Is Nothing Then ReDim output(0) Else ReDim Preserve output(output.Length)
                    output(output.Length - 1) = entry
                End If
            End If
        Next

        If ReturnRelativePaths Then
            ' Extract the last component of the path
            output = PathLast(output)
        End If

        Return output
    End Function
    Public Function GetDirectories(ByVal path As String) As String()
        Return GetDirectories(path, "*")
    End Function
    Public Function GetDirectories() As String()
        Return GetDirectories("/", "*")
    End Function
    Public Function GetFile(ByVal filename As String) As Utilities.PackagedFileInformationCollection

        ' Process and Validate filename
        filename = LCase(filename)
        filename = Replace(filename, "/", "\")
        filename = Replace(filename, "\\", "\")
        If Left(filename, 1) = "\" Then filename = Right(filename, filename.Length - 1) ' Remove any heading '\'
        If Right(filename, 1) = "\" Then filename = Left(filename, filename.Length - 1) ' Remove any leading '\'

        ' Get the ammount of levels that filename have
        Dim level As String
        Dim temp() As String
        temp = Split(filename, "\")
        level = temp.Length - 1
        Erase temp

        If level = -1 Then level = 0

        If _fileTreeBuilt = False Then BuildFileTreeStructure() ' Build File structure if its not built before

        If level >= _formattedFileTree.Length Then Return Nothing ' No files on that level
        If _formattedFileTree(level) Is Nothing Then Return Nothing

        For Each entry As PackagedFileInformationCollection In _formattedFileTree(level)
            If Trim(LCase(entry.FullName)) = Trim(LCase(filename)) Then Return entry
        Next

        ' No file found
        Return Nothing
    End Function
    Public Function GetFiles(ByVal path As String, ByVal filter As String) As Utilities.PackagedFileInformationCollection()

        ' Process and Validate Path
        path = LCase(path)
        path = Replace(path, "/", "\")
        path = Replace(path, "\\", "\")
        If Left(path, 1) = "\" Then path = Right(path, path.Length - 1) ' Remove any heading '\'
        If Right(path, 1) = "\" Then path = Left(path, path.Length - 1) ' Remove any leading '\'

        ' Get the ammount of levels that Path have
        Dim level As String
        If path = "" Then
            level = 0
        Else
            Dim temp() As String
            temp = Split(path, "\")
            level = temp.Length
            Erase temp
        End If

        If level = -1 Then level = 0

        If _fileTreeBuilt = False Then BuildFileTreeStructure() ' Build File structure if its not built before

        If level > _formattedFileTree.Length - 1 Then Return Nothing ' No files on that level

        filter = Trim(filter)
        If filter = "" Then filter = "*"

        Dim files() As PackagedFileInformationCollection = _formattedFileTree(level)
        If files Is Nothing Then Return Nothing
        Dim output() As PackagedFileInformationCollection = Nothing
        Dim parsedFilter As String = Replace(filter, "[", "[[]")
        For Each entry As PackagedFileInformationCollection In files
            Dim myEntry As String = entry.Directory
            If Left(myEntry, 1) = "\" Then myEntry = Right(myEntry, myEntry.Length - 1) ' Remove any heading '\'
            If Right(myEntry, 1) = "\" Then myEntry = Left(myEntry, myEntry.Length - 1) ' Remove any leading '\'
            myEntry = Trim(myEntry)
            If LCase(myEntry) = LCase(path) Then
                If LCase(entry.Filename) Like LCase(parsedFilter) Then
                    If output Is Nothing Then ReDim output(0) Else ReDim Preserve output(output.Length)
                    output(output.Length - 1) = entry
                End If
            End If
        Next

        Return output
    End Function
    Public Function GetFiles(ByVal path As String) As Utilities.PackagedFileInformationCollection()
        Return GetFiles(path, "*")
    End Function
    Public Function GetFiles() As Utilities.PackagedFileInformationCollection()
        Return GetFiles("\", "*")
    End Function
    Private _directoryTreeBuilt As Boolean
    Private _fileTreeBuilt As Boolean
    Private _formattedDirectoryTree()() As String
    Private _formattedFileTree()() As PackagedFileInformationCollection
    Private _rawDirectoryTree() As String
    Private _rawFileTree() As String
    Public ReadOnly Property IsDirectoryTreeBuilt As Boolean
        Get
            Return _directoryTreeBuilt
        End Get
    End Property
    Public ReadOnly Property IsFileTreeBuilt As Boolean
        Get
            Return _fileTreeBuilt
        End Get
    End Property
    Public ReadOnly Property DirectoryTree() As String()
        Get
            Return _rawDirectoryTree
        End Get
    End Property
    Public ReadOnly Property FileTree() As String()
        Get
            Return _rawFileTree
        End Get
    End Property
    Public Sub BuildDirectoryTreeStructure(ByVal forceRebuild As Boolean)
        ' Build the entire Directory Tree Structure for the current Package

        If (_directoryTreeBuilt = True) AndAlso (forceRebuild = False) Then Exit Sub

        _AddLog("Building Virtrualized Directory Tree structure...")

        ' Erase any previous tree
        _directoryTreeBuilt = False
        Erase _formattedDirectoryTree
        Erase _rawDirectoryTree

        Dim newFormattedTree()() As String = Nothing
        Dim newRawTree() As String = Nothing

        '' Insert a dummy entry for \ with level 0
        'ReDim newFormattedTree(0)
        'ReDim newFormattedTree(0)(0)
        'newFormattedTree(0)(0) = "\"

        ' Get all directories specified by file paths
        Dim directories(MountedPackage.File.Length - 1) As String
        Dim i As Integer
        For i = 0 To MountedPackage.File.Length - 1
            directories(i) = MountedPackage.File(i).Directory
        Next

        _AddLog("Processing files...")
        ' Process each directory
        For Each entry As String In directories
            If Not entry = "\" Then
                If Left(entry, 1) = "\" Then entry = Right(entry, entry.Length - 1) ' Remove any heading '\'
                If Right(entry, 1) = "\" Then entry = Left(entry, entry.Length - 1) ' Remove any leading '\'
                Dim level As Integer
                Dim components() As String = Split(entry, "\")
                level = components.Length
                ' Parse each component path
                Dim currComponent As Integer
                For currComponent = components.Length - 1 To 0 Step -1 ' Inverse loop
                    Dim prefix As String = ""
                    If Not currComponent = 0 Then
                        prefix = ConsolidateParameters(components, 0, currComponent - 1, "\")
                        prefix &= "\"
                    End If
                    level = currComponent + 1
                    If level = 0 Then level = 1
                    Dim search As Boolean = True
                    If newFormattedTree Is Nothing Then
                        ReDim newFormattedTree(level)
                        search = False
                    Else
                        If level >= newFormattedTree.Length Then
                            ' Resize the Tree to fit level
                            ReDim Preserve newFormattedTree(level)
                            search = False
                        End If
                    End If

                    If search = True Then
                        If _CheckArrayItem(newFormattedTree(level), prefix & components(currComponent)) = True Then GoTo SkipItem
                    End If
                    If newFormattedTree(level) Is Nothing Then ReDim newFormattedTree(level)(0) Else ReDim Preserve newFormattedTree(level)(newFormattedTree(level).Length)
                    newFormattedTree(level)(newFormattedTree(level).Length - 1) = prefix & components(currComponent)
SkipItem:
                Next
            End If
        Next

        _AddLog("Consolidating structure...")
        If newFormattedTree Is Nothing Then GoTo SkipConsolidate
        ' Create the RAW Tree
        For i = 0 To newFormattedTree.Length - 1
            ' Each level
            Dim x As Integer
            If newFormattedTree(i) IsNot Nothing Then
                For x = 0 To newFormattedTree(i).Length - 1
                    ' Each directory on that level
                    If newRawTree Is Nothing Then ReDim newRawTree(0) Else ReDim Preserve newRawTree(newRawTree.Length)
                    newRawTree(newRawTree.Length - 1) = newFormattedTree(i)(x)
                Next
            End If
        Next

SkipConsolidate:

        _rawDirectoryTree = newRawTree
        _formattedDirectoryTree = newFormattedTree
        _directoryTreeBuilt = True

        _AddLog("Done.")
    End Sub
    Public Sub BuildDirectoryTreeStructure()
        BuildDirectoryTreeStructure(False)
    End Sub
    Public Sub BuildFileTreeStructure(ByVal forceRebuild As Boolean)
        ' Build the entire File Tree Structure for the current Package

        If (_fileTreeBuilt = True) And (forceRebuild = False) Then Exit Sub

        _AddLog("Building Virtrualized File Tree structure...")

        ' Erase any previous tree
        _fileTreeBuilt = False
        Erase _formattedFileTree
        Erase _rawFileTree

        Dim newFormattedTree()() As PackagedFileInformationCollection = Nothing
        Dim newRawTree() As String = Nothing

        '' Insert a dummy entry for \ with level 0
        'ReDim newFormattedTree(0)
        'ReDim newFormattedTree(0)(0)
        'newFormattedTree(0)(0) = Nothing

        ' Get and process all file paths
        _AddLog("Processing files...")
        Dim i As Integer
        For i = 0 To MountedPackage.File.Length - 1
            Dim level As Integer
            level = Split(MountedPackage.File(i).FullName, "\").Length - 1
            If level = -1 Then level = 0
            If newFormattedTree Is Nothing Then
                ReDim newFormattedTree(level)
            End If
            If newFormattedTree Is Nothing Then
                ReDim newFormattedTree(level)
            Else
                If level >= newFormattedTree.Length Then
                    ' Resize the Tree to fit level
                    ReDim Preserve newFormattedTree(level)
                End If
            End If
            If newFormattedTree(level) Is Nothing Then ReDim newFormattedTree(level)(0) Else ReDim Preserve newFormattedTree(level)(newFormattedTree(level).Length)
            newFormattedTree(level)(newFormattedTree(level).Length - 1) = MountedPackage.File(i)
        Next

        _AddLog("Consolidating structure...")
        If newFormattedTree Is Nothing Then GoTo SkipConsolidate
        ' Create the RAW Tree
        For i = 0 To newFormattedTree.Length - 1
            ' Each level
            Dim x As Integer
            If newFormattedTree(i) IsNot Nothing Then
                For x = 0 To newFormattedTree(i).Length - 1
                    ' Each directory on that level
                    If Not newFormattedTree(i)(x).FullName = "" Then
                        If newRawTree Is Nothing Then ReDim newRawTree(0) Else ReDim Preserve newRawTree(newRawTree.Length)
                        newRawTree(newRawTree.Length - 1) = newFormattedTree(i)(x).FullName
                    End If
                Next
            End If
        Next

SkipConsolidate:

        _rawFileTree = newRawTree
        _formattedFileTree = newFormattedTree
        _fileTreeBuilt = True

        _AddLog("Done.")
    End Sub
    Public Sub BuildFileTreeStructure()
        BuildFileTreeStructure(False)
    End Sub

    Private Function _CheckArrayItem(ByVal array As String(), ByVal entry As String) As Boolean
        ' Checks if a similar entry is already inside the specified array

        If array Is Nothing Then Return False
        If entry = "" Then Return True
        If array.Length = 0 Then Return False

        entry = LCase(Trim(entry))

        For Each x As String In array
            If LCase(Trim(x)) = entry Then Return True
        Next

        Return False
    End Function
    'Private Function _CheckArrayItem(ByVal array As PackagedFileInformationCollection(), ByVal entry As PackagedFileInformationCollection) As Boolean
    '    ' Checks if a similar entry is already inside the specified array

    '    If array Is Nothing Then Return False
    '    If array.Length = 0 Then Return False

    '    Try
    '        Dim filename As String = entry.FullName
    '        filename = Trim(LCase(filename))
    '        For Each x As PackagedFileInformationCollection In array
    '            If Trim(LCase(x.FullName)) = filename Then Return True
    '        Next
    '    Catch
    '        Return True
    '    End Try

    '    Return False
    'End Function

    ' System.IDisposable Implementation

    ' Global File Validation
    Public Structure FileValidationInformation
        Private targetEngine As Engine
        Private fileIndex As ULong

        Friend Sub New(ByRef pEngine As Engine, ByVal pFileIndex As ULong)
            targetEngine = pEngine
            fileIndex = pFileIndex
        End Sub

        Public ReadOnly Property ValidationPerformed As Boolean
            Get
                Return targetEngine.MountedPackage.File(fileIndex).ValidationPerformed
            End Get
        End Property
        Public ReadOnly Property Validated As Boolean
            Get
                Return targetEngine.MountedPackage.File(fileIndex).Validated
            End Get
        End Property
        Public ReadOnly Property Filename As String
            Get
                Return targetEngine.MountedPackage.File(fileIndex).Filename
            End Get
        End Property
        Public ReadOnly Property Directory As String
            Get
                Return targetEngine.MountedPackage.File(fileIndex).Directory
            End Get
        End Property
        Public ReadOnly Property FullName As String
            Get
                Return targetEngine.MountedPackage.File(fileIndex).FullName
            End Get
        End Property
        Public ReadOnly Property Length As ULong
            Get
                Return targetEngine.MountedPackage.File(fileIndex).Length
            End Get
        End Property

        Public Function Validate() As Boolean
            Dim item As PackagedFileInformationCollection = targetEngine._packageTable._file.Item(fileIndex)
            Dim returned As Boolean
            returned = item.Validate()
            targetEngine._packageTable._file.Item(fileIndex) = item

            Return returned
        End Function
    End Structure
    Public ReadOnly Property FileValidation(ByVal fileIndex As ULong) As FileValidationInformation
        Get
            Return New FileValidationInformation(Me, fileIndex)
        End Get
    End Property

    ' Disposeable Support
    Private _disposed As Boolean
    Public ReadOnly Property Disposed As Boolean
        Get
            Return _disposed
        End Get
    End Property
    Public Sub Dispose() Implements System.IDisposable.Dispose
        Dispose(True)
    End Sub
    Private Sub Dispose(ByVal userLand As Boolean)
        If _disposed = True Then Exit Sub
        If userLand = True Then
            ' Dispose managed code
            _loggingFacility.ForceEventTriggering = False
            Unmount()
            _loggingFacility = Nothing
        End If

        ' Dispose Unmanaged code and Disposeable resources
        _CloseStream(_ioDirectStream, _ioStreamReader)

        ' Disable Garbage Collector from calling Dispose again when destroying this class
        GC.SuppressFinalize(Me)

        _disposed = True
    End Sub

    Protected Overrides Sub Finalize()
        If _disposed = False Then Dispose(False)
        MyBase.Finalize()
    End Sub

    Private Function PathLast(ByVal paths() As String) As String()
        ' Returns an array with only the last component of each path specified
        ' aka: Generates relative paths

        If paths Is Nothing Then Return Nothing
        If paths.Length = 0 Then Return Nothing

        Dim i As Integer
        For i = 0 To paths.Length - 1
            If paths(i) = "" Then paths(i) = "\"

            If Not paths(i) = "\" Then
                Dim tmp() As String = Split(paths(i), "\")
                If (tmp IsNot Nothing) And (tmp.Length > 0) Then
                    paths(i) = tmp(tmp.Length - 1)
                Else
                    paths(i) = "\"
                End If
            End If
        Next

        Return paths
    End Function
End Class







'Public Module XDEBUG
'    Public Function DEBUG_DUMP(ByVal buffer() As Byte) As String
'        Dim i As Integer
'        Dim output As String = ""
'        For i = 0 To buffer.Length - 1
'            Dim octect As String = Hex(buffer(i))
'            If octect.Length = 1 Then octect = "0" & octect
'            output &= octect
'        Next

'        output = UCase(output)
'        Return output
'    End Function
'End Module